﻿using Devil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using TableCore;

namespace TableExec
{
    enum EAction
    {
        GenCode = 1,
        GenData = 2,
        Help = 4,
        FillVairant = 8,
        PrintFormatter = 16,
        //GenSingleFile = 8,
    }

    class Program
    {
        public class Category
        {
            public string category { get; private set; }
            public string foler;
            public string csFolder;
            public Category(string cate)
            {
                category = cate;
            }
        }

        const int ERROR_GEN_CODE = 1;
        const int ERROR_GEN_DATA = 2;
        const int ERROR_USE_TABLE = 4;
        const int ERROR_PARSE_ARG = 8;
        const int ERROR_OPEN_FILE = 16;
        const int ERROR_CONFIG = 32;

        static Args mArgs;
        //static List<GTFile.Filter> mFilters = new List<GTFile.Filter>();

        struct Args
        {
            public string root;
            public string dataOutput;
            public string codeOutput;
            public string input;

            public EAction action;
            public bool interact;
            public string logFile;
            public bool appendLog;
            public ELogLv logLv;
            public bool clean;
            public string filter;
            public bool merge;
            public GTFile.EMergeType mergeType;
            public string primary;
            public string types;
            public bool skipEmptyData;

            public static Args GetArgs(string[] args)
            {
                if (args == null || args.Length == 0)
                    throw new Exception("参数错误。");
                Args arg = new Args();
                //mLogLv = ELogLv.Info;
                bool pathMode = false;
                for (int i = 0; i < args.Length; i++)
                {
                    var str = args[i].ToLower();
                    switch (str)
                    {
                        case "-root":
                        case "/root":
                            arg.root = args[++i];
                            break;
                        case "-data":
                        case "/data":
                            arg.dataOutput = args[++i];
                            break;
                        case "-code":
                        case "/code":
                            arg.codeOutput = args[++i];
                            break;
                        case "-help":
                        case "/help":
                            arg.action |= EAction.Help;
                            break;
                        case "-formatter":
                        case "/formatter":
                            arg.action |= EAction.PrintFormatter;
                            break;
                        case "/mode":
                        case "-mode":
                            var mod = args[++i].ToLower();
                            arg.interact = mod.IndexOf('i') >= 0;
                            arg.appendLog = mod.IndexOf('a') >= 0;
                            if (mod.IndexOf('e') >= 0)
                                arg.logLv = ELogLv.Error;
                            else if (mod.IndexOf('w') >= 0)
                                arg.logLv = ELogLv.Warning;
                            arg.clean = mod.IndexOf('c') >= 0;
                            var ex = mod.IndexOf('g');
                            arg.merge = ex >= 0;
                            if (arg.merge && ex > 0)
                            {
                                var prev = mod[ex - 1];
                                arg.mergeType = prev == '-' ? GTFile.EMergeType.Gather : (prev == '+' ? GTFile.EMergeType.Extract : GTFile.EMergeType.Default);
                            }
                            else
                            {
                                arg.mergeType = GTFile.EMergeType.Default;
                            }
                            if (mod.IndexOf('p') >= 0)
                                pathMode = true;
                            if (mod.IndexOf('s') >= 0)
                                arg.skipEmptyData = true;
                            if (mod.IndexOf('v') >= 0)
                                arg.action |= EAction.FillVairant;
                            break;
                        case "/log":
                        case "-log":
                            arg.logFile = args[++i];
                            break;
                        case "/filter":
                        case "-filter":
                            arg.filter = args[++i];
                            break;
                        case "/primary":
                        case "-primary":
                            arg.primary = args[++i];
                            break;
                        case "/types":
                        case "-types":
                            arg.types = args[++i];
                            break;
                        default:
                            arg.input = args[i];
                            if (i != args.Length - 1)
                                throw new Exception("参数错误");
                            break;
                    }
                }

                Regex obsolutePath = new Regex(@"^[c-z]:\\.*", RegexOptions.IgnoreCase);
                if (string.IsNullOrEmpty(arg.root))
                    arg.root = Environment.CurrentDirectory;
                if (!obsolutePath.IsMatch(arg.root))
                    arg.root = Path.GetFullPath(arg.root);
                var contextPath = pathMode ? Environment.CurrentDirectory : arg.root;
             
                if (!string.IsNullOrEmpty(arg.codeOutput))
                {
                    arg.action |= EAction.GenCode;
                    if (!obsolutePath.IsMatch(arg.codeOutput))
                        arg.codeOutput = Path.GetFullPath(Path.Combine(contextPath, arg.codeOutput));
                }
                if (!string.IsNullOrEmpty(arg.dataOutput))
                {
                    arg.action |= EAction.GenData;
                    if (!obsolutePath.IsMatch(arg.dataOutput))
                        arg.dataOutput = Path.GetFullPath(Path.Combine(contextPath, arg.dataOutput));
                }
                var task = EAction.GenCode | EAction.GenData | EAction.FillVairant;
                if (!arg.interact && (arg.action & task) != 0)
                {
                    if (string.IsNullOrEmpty(arg.input))
                        throw new Exception("参数错误");
                    if (!obsolutePath.IsMatch(arg.input))
                        arg.input = Path.GetFullPath(Path.Combine(arg.root, arg.input));
                }

                if (!string.IsNullOrEmpty(arg.filter) && !obsolutePath.IsMatch(arg.filter))
                    arg.filter = Path.GetFullPath(Path.Combine(contextPath, arg.filter));
                if (!string.IsNullOrEmpty(arg.logFile) && !obsolutePath.IsMatch(arg.logFile))
                    arg.logFile = Path.GetFullPath(Path.Combine(contextPath, arg.logFile));
                if (!string.IsNullOrEmpty(arg.types) && !obsolutePath.IsMatch(arg.types))
                    arg.types = Path.GetFullPath(Path.Combine(contextPath, arg.types));
                return arg;
            }
        }

        //static ThreadFlag mainFlag;

        static bool IsComplete(GTFile gtfile)
        {
            return gtfile == null || gtfile.IsComplete();
        }

        static int Main(string[] args)
        {
            int error = 0;
            var task = EAction.GenCode | EAction.GenData | EAction.FillVairant;
            var exec = task | EAction.PrintFormatter;
            try
            {
                mArgs = Args.GetArgs(args);
            }
            catch (Exception e)
            {
                Debug.LogFormat("[ERROR] {0}\n{1}", e, StringUtil.LinkString(false, " ", args));
                error |= ERROR_PARSE_ARG;
            }
            if (error != 0 || (mArgs.action & EAction.Help) != 0)
            {
                var help = Utils.ReadFile(Utils.GetRelativePath("./Config/ReadME.txt"));
                Debug.Log(help);
                Debug.Log("[Press any key to exit]");
                Console.ReadKey();
            }
            else if (error == 0 && (mArgs.action & exec) != 0)
            {
                var executeTask = (mArgs.action & task) != 0;
                //Debug.LogFormat("Will Parse \"{0}\"", mInput);
                ParallelDispatcher.InitApplicationData();

                ParallelDispatcher.Handler job = default;
                if((mArgs.action & EAction.PrintFormatter)!= 0)
                {
                    var fac = Factory.GetInstance();
                    job = ParallelUtils.Schedule(fac.PrintFormatters, false, fac.GetInitializeHandler());
                }
                if (executeTask)
                {
                    Debug.StartLogFile(mArgs.logLv, mArgs.logFile, mArgs.appendLog);
                    if (mArgs.interact)
                    {
                        Debug.LogFormat("交互式数据导出（工作路径{0}）", mArgs.root);
                    }
                }

                long t = DateTime.UtcNow.Ticks;
                GTFile gtfile = null;
                Regex obsolutePath = new Regex(@"^[c-z]:\\.*", RegexOptions.IgnoreCase);
                if (executeTask)
                {
                    if (!string.IsNullOrEmpty(mArgs.types))
                        Factory.LibFolder = mArgs.types;
                    gtfile = new GTFile(mArgs.root, mArgs.dataOutput, mArgs.codeOutput);
                    gtfile.FilterFile = mArgs.filter;
                    gtfile.CleanExports = mArgs.clean;
                    gtfile.SkipEmptyData = mArgs.skipEmptyData;
                    if (!mArgs.interact)
                    {
                        StartUpload(gtfile, mArgs);
                    }
                }
                while (job.IsValid || mArgs.interact || !IsComplete(gtfile))
                {
                    if (!job.IsValid && gtfile != null && gtfile.IsComplete())
                    {
                        Console.WriteLine();
                        Debug.LogWarningFormat("请输入导出文件或文件夹：(如果要退出请输入Q)");
                        var line = Console.ReadLine();
                        if (string.IsNullOrEmpty(line))
                            continue;
                        var c0 = line[0];
                        var c1 = line.Length > 2 ? line[line.Length - 1] : '-';
                        if(c0 == c1 && (c0 == '"'|| c0 == '\''))
                        {
                            line = line.Substring(1, line.Length - 2).Trim();
                        }
                        if (string.IsNullOrEmpty(line))
                            continue;
                        if(StringUtil.EqualIgnoreCase(line, "q"))
                        {
                            break;
                        }
                        if (!obsolutePath.IsMatch(line))
                            line = Path.Combine(mArgs.root, line);
                        if(!File.Exists(line) && !Directory.Exists(line))
                        {
                            Debug.LogErrorFormat("不存在文件或文件夹：{0}", line);
                            continue;
                        }
                        mArgs.input = line;
                        StartUpload(gtfile, mArgs);
                        //if (mArgs.merge)
                        //    StartMerge(gtfile, mArgs);
                        //else
                        //    StartUpload(gtfile, line);
                    }
                    else
                    {
                        Thread.Sleep(30);
                    }
                    var current = DateTime.UtcNow.Ticks;
                    var dt = (current - t) * ParallelDispatcher.tick_2_seconds;
                    t = current;
                    ParallelDispatcher.MainUpdate((float)dt);
                }
                if (executeTask)
                    Debug.StopLogFile();
                ParallelDispatcher.GetInstance()?.Dispose();
            }
            else
            {
                Debug.Log("Nothing need to do.");
            }
            return error;
        }


        static void UploadFile(GTFile gtfile, string file, Args arg, bool useFilter)
        {
            if ((arg.action & EAction.FillVairant) != 0)
                gtfile.UploadFillVariantsFile(file, useFilter);
            else if (arg.merge)
                gtfile.UploadMergeFile(file, arg.primary, arg.mergeType, true);
            else
                gtfile.UploadExportFile(file, useFilter);
        }

        static void StartUpload(GTFile gtfile, Args arg)
        {
            var validFile = new Regex(@".+\.xls(x)?", RegexOptions.IgnoreCase);
            if (File.Exists(arg.input))
            {
                if (validFile.IsMatch(arg.input))
                    UploadFile(gtfile, arg.input, arg, false);
            }
            else if (Directory.Exists(arg.input))
            {
                Stack<DirectoryInfo> folders = new Stack<DirectoryInfo>();
                folders.Push(new DirectoryInfo(arg.input));
                while (folders.Count > 0)
                {
                    var dic = folders.Pop();
                    var files = dic.GetFiles();
                    for (int i = 0; i < files.Length; i++)
                    {
                        var f = files[i];
                        if (!validFile.IsMatch(f.FullName))
                            continue;
                        UploadFile(gtfile, f.FullName, arg, true);
                    }
                    var dics = dic.GetDirectories();
                    for (int i = 0; i < dics.Length; i++)
                    {
                        folders.Push(dics[i]);
                    }
                }
            }
            gtfile.FinishUpload();
        }

        //static void StartMerge(GTFile gtfile, Args arg)
        //{
        //    var validFile = new Regex(@".+\.xls(x)?", RegexOptions.IgnoreCase);
        //    if (File.Exists(arg.input))
        //    {
        //        if (validFile.IsMatch(arg.input))
        //            gtfile.UploadMergeFile(arg.input, arg.primary, arg.mergeType, false);
        //    }
        //    else if (Directory.Exists(arg.input))
        //    {
        //        Stack<DirectoryInfo> folders = new Stack<DirectoryInfo>();
        //        folders.Push(new DirectoryInfo(arg.input));
        //        while (folders.Count > 0)
        //        {
        //            var dic = folders.Pop();
        //            var files = dic.GetFiles();
        //            for (int i = 0; i < files.Length; i++)
        //            {
        //                var f = files[i];
        //                if (!validFile.IsMatch(f.FullName))
        //                    continue;
        //                gtfile.UploadMergeFile(f.FullName, arg.primary, arg.mergeType, true);
        //            }
        //            var dics = dic.GetDirectories();
        //            for (int i = 0; i < dics.Length; i++)
        //            {
        //                folders.Push(dics[i]);
        //            }
        //        }
        //    }
        //    gtfile.FinishUpload();
        //}

        //static void StartUpload(GTFile gtfile, string file)
        //{
        //    var validFile = new Regex(@".+\.xls(x)?", RegexOptions.IgnoreCase);
        //    if (File.Exists(file))
        //    {
        //        if (validFile.IsMatch(file))
        //            gtfile.UploadExportFile(file, false);
        //    }
        //    else if (Directory.Exists(file))
        //    {
        //        Stack<DirectoryInfo> folders = new Stack<DirectoryInfo>();
        //        folders.Push(new DirectoryInfo(file));
        //        while (folders.Count > 0)
        //        {
        //            var dic = folders.Pop();
        //            var files = dic.GetFiles();
        //            for (int i = 0; i < files.Length; i++)
        //            {
        //                var f = files[i];
        //                if (!validFile.IsMatch(f.FullName))
        //                    continue;
        //                gtfile.UploadExportFile(f.FullName, true);
        //            }
        //            var dics = dic.GetDirectories();
        //            for (int i = 0; i < dics.Length; i++)
        //            {
        //                folders.Push(dics[i]);
        //            }
        //        }
        //    }
        //    gtfile.FinishUpload();
        //}
    }
}
